#include <keller30drv.h>

/* 
 * Return driver's version
*/
char *get_driver_version() 
{
	return KDRV_VERSION;
}

/*
 * CRC-16 calculation
 * Calculation of CRC-16 checksum over an amount of bytes in a buffer. 
 * The calculation is done without the 2byte from crc16 (receive-mode). 
 *
 * SC_Buffer[]: Byte-Buffer. Type: unsigned char (8bit) 
 * SC_Amount :  Amount of Bytes to calculate checksum on
**/
void CalcCRC16(unsigned char *SC_Buffer, int SC_Amount, unsigned char* CRC_H, unsigned char* CRC_L)
{
	// locals 
	unsigned int Crc;
	unsigned char n, m, x;

	// initialisation 
	Crc= 0xFFFF;
	m= SC_Amount;
	x= 0;

        // loop over all bits 
        while(m>0)
        {

        Crc^= SC_Buffer[x];
        for(n=0; n<8; n++)
        {
                if(Crc&1)
                {
                        Crc>>= 1;
                        Crc^= 0xA001;
                }
                else
                        Crc>>= 1;
        }
        m--;
        x++;

        }
        // result 
        *CRC_H= (Crc>>8)&0xFF;
        *CRC_L= Crc&0xFF;
}


/* Function 73 :  Read value of a channel (floating point) */
float f73_read_channel(int ttyfd, int slave, int timeout, int channel, int *error)
{
       int bytes_required;
       int bytes_received = 0;
       char rxchar = -1;
       unsigned char recv[1024];
       time_t start;
       time_t finish;
       int diff_time;
       unsigned char message[6];  // message
       int req_msg_size = (sizeof(message)-1);
       int rsp_msg_size = 0;
       unsigned char crc_byteH = 0;
       unsigned char crc_byteL = 0;
       int function;
       unsigned char return_value[4];	
       float *float_ptr;	
       
       bytes_required = (sizeof(struct f73_response_s) + req_msg_size);    // expected response size if echo enabled

       /* Prepare message to be sent */

       function = 73;
       message[0] = slave;       // slave address
       message[1] = function;    // function id
       message[2] = channel;     // channel (sensor id)

       CalcCRC16(message, 3, &crc_byteH, &crc_byteL); // calculate CRC16 for the message data

       message[3] = crc_byteH;	 // CRC HighByte
       message[4] = crc_byteL;   // CRC LowByte
       message[5] = (unsigned char)NULL;
       req_msg_size = 5;

       #ifdef DEBUG
       fprintf(stderr, "Request message bytes:\n");
       int cnt;
       for(cnt=0; cnt <= req_msg_size; cnt++) {
            fprintf(stderr, "Byte no.%d == [%d] (%p)\n",cnt, message[cnt], message[cnt]);
       }
       fprintf(stderr, "\n");
       #endif

       /* Send prepared message to the slave device */

       ioctl(ttyfd,TCFLSH,0);    // clear serial input buffer
       ioctl(ttyfd,TCFLSH,1);    // clear serial output buffer
       write(ttyfd,message,req_msg_size);   // send data to slave
       ioctl(ttyfd,TCFLSH,0);    // clear serial input buffer
       time(&start);		 // set the start timestamp for timeout check
            
       /* Receive response from slave */

       bytes_received = 0;
       while (bytes_received < bytes_required)
       {         
		rxchar = -1;
		read(ttyfd,&rxchar,1);
         
		if (rxchar != -1)
		{
			#ifdef DEBUG
			fprintf(stderr, "Received char no. %d = [%d] (%p)\n",bytes_received, rxchar, rxchar);
			#endif

			bytes_received++;
			if (bytes_received > req_msg_size) {
			   recv[rsp_msg_size++] = rxchar;  
			} else {
			   #ifdef DEBUG
			   fprintf(stderr, "skipping...\n");
			   #endif
			}
		}  
		// timeout check
		time(&finish);	
		diff_time = difftime(finish,start);
		if(diff_time > timeout)		
		{
			#ifdef DEBUG
			fprintf(stderr, "timeout\n");
			#endif
			*error = KDRV_TIMEOUT_ERROR;
			break;
		}
       } 

       /* Check for exception */

       if (bytes_received < bytes_required)
       {
		if (recv[1] != function)
		{
			#ifdef DEBUG
			fprintf(stderr, "exception error\n");
			#endif
			*error = KDRV_EXCEPTION_ERROR;
			return recv[2];
		}    
       }
	
       /* Decode response message */

       if (bytes_required == bytes_received)
       {
		/* Calculate and check CRC on the response message */
	        CalcCRC16(recv, rsp_msg_size-2, &crc_byteH, &crc_byteL);
	        struct f73_response_s *rsp_msg;
	        rsp_msg = (struct f73_response_s *) recv; 

		if ( (crc_byteH == rsp_msg->crc16_H) && (crc_byteL == rsp_msg->crc16_L) ) {
			#ifdef DEBUG
			fprintf(stderr, "Match!\n");
			#endif
			memset(return_value, 0, sizeof(return_value)); // empty array
			return_value[0] = rsp_msg->byte0; // save received number in right order (little endian)
			return_value[1] = rsp_msg->byte1;
			return_value[2] = rsp_msg->byte2;
			return_value[3] = rsp_msg->byte3;
			float_ptr = (float *)return_value; // cast prepared number as float
			*error = KDRV_SUCCESS;
			return *float_ptr; // return float
		} else {
			#ifdef DEBUG
			fprintf(stderr, "Checksum mismatch!\n");
			fprintf(stderr, "[%x%x] != [%x%x]\n", crc_byteH, crc_byteL, rsp_msg->crc16_H, rsp_msg->crc16_L);
			#endif
			*error = KDRV_CRC_ERROR;
		}
      }  
	
      /* Request failed */
      return FALSE;
} 
               
/* Function 74 : Read value of a channel (integer) */
long f74_read_channel(int ttyfd, int slave, int timeout, int channel, int *error)
{
       int bytes_required;
       int bytes_received = 0;
       char rxchar = -1;
       unsigned char recv[1024];
       time_t start;
       time_t finish;
       int diff_time;
       unsigned char message[6];  // message
       int req_msg_size = (sizeof(message)-1);
       int rsp_msg_size = 0;
       unsigned char crc_byteH = 0;
       unsigned char crc_byteL = 0;
       int function;
       unsigned char return_value[sizeof(long)];
       long *long_ptr;	
       
       bytes_required = (sizeof(struct f74_response_s) + req_msg_size);    // expected response size if echo enabled

       /* Prepare message to be sent */

       function = 74;
       message[0] = slave;       // slave address
       message[1] = function;    // function id
       message[2] = channel;     // channel (sensor id)

       CalcCRC16(message, 3, &crc_byteH, &crc_byteL); // calculate CRC16 for the message data

       message[3] = crc_byteH;	 // CRC HighByte
       message[4] = crc_byteL;   // CRC LowByte
       message[5] = (unsigned char)NULL;
       req_msg_size = 5;

       #ifdef DEBUG
       int cnt;
       fprintf(stderr, "Request message bytes:\n");
       for(cnt=0; cnt <= req_msg_size; cnt++) {
            fprintf(stderr, "Byte no.%d == [%d] (%p)\n",cnt, message[cnt], message[cnt]);
       }
       fprintf(stderr, "\n");
       #endif

       /* Send prepared message to the slave device */

       ioctl(ttyfd,TCFLSH,0);    // clear serial input buffer
       ioctl(ttyfd,TCFLSH,1);    // clear serial output buffer
       write(ttyfd,message,req_msg_size);   // send data to slave
       ioctl(ttyfd,TCFLSH,0);    // clear serial input buffer
       time(&start);		 // set the start timestamp for timeout check
            
       /* Receive response from slave */

       bytes_received = 0;
       while (bytes_received < bytes_required)
       {         
		rxchar = -1;
		read(ttyfd,&rxchar,1);
         
		if (rxchar != -1)
		{
			#ifdef DEBUG
			fprintf(stderr, "Received char no. %d = [%d] (%p)\n",bytes_received, rxchar, rxchar);
			#endif

			bytes_received++;
			if (bytes_received > req_msg_size) {
			   recv[rsp_msg_size++] = rxchar;  
			} else {
			   #ifdef DEBUG
			   fprintf(stderr, "skipping...\n");
			   #endif
			}
		}  
		// timeout check
		time(&finish);	
		diff_time = difftime(finish,start);
		if(diff_time > timeout)		
		{
			#ifdef DEBUG
			fprintf(stderr, "timeout \n");
			#endif
			*error = KDRV_TIMEOUT_ERROR;
			break;
		}
       } 
               
      /* Check for exception */
      
      if (bytes_received < bytes_required)
      {
		if (recv[1] != function)
		{
			#ifdef DEBUG
			fprintf(stderr, "exception response \n");
			#endif
			*error = KDRV_EXCEPTION_ERROR;
			return recv[2]; // return exception code
		}    
      }

       /* Decode response message */

       if (bytes_required == bytes_received)
       {
		/* Calculate and check CRC on the response message */
	        CalcCRC16(recv, rsp_msg_size-2, &crc_byteH, &crc_byteL);
	        struct f74_response_s *rsp_msg;
	        rsp_msg = (struct f74_response_s *) recv; 

		if ( (crc_byteH == rsp_msg->crc16_H) && (crc_byteL == rsp_msg->crc16_L) ) {
			#ifdef DEBUG
			fprintf(stderr, "Match!\n");
			#endif
			memset(return_value, 0, sizeof(return_value)); // empty array
			return_value[0] = rsp_msg->byte0; // save received bytes in right order (little endian)
			return_value[1] = rsp_msg->byte1;
			return_value[2] = rsp_msg->byte2;
			return_value[3] = rsp_msg->byte3;
			long_ptr = (long *)return_value;  // cast prepared number as long
			*error = KDRV_SUCCESS;
			return *long_ptr; // return long number
		} else {
			#ifdef DEBUG
			fprintf(stderr, "Checksum mismatch!\n");
			fprintf(stderr, "[%x%x] != [%x%x]\n", crc_byteH, crc_byteL, rsp_msg->crc16_H, rsp_msg->crc16_L);
			#endif
			*error = KDRV_CRC_ERROR;
		}
      }  
              
      /* Request failed */
      return FALSE;
} 
  
/* Function 48 : Initialise and release */
long f48_initialise(int ttyfd, int slave, int timeout, struct f48_response_s *f48_response, int *error)
{
       int bytes_required;
       int bytes_received = 0;
       char rxchar = -1;
       unsigned char recv[1024];
       time_t start;
       time_t finish;
       int diff_time;
       unsigned char message[5];  // message of size data+NULL
       int req_msg_size = (sizeof(message)-1);
       int rsp_msg_size = 0;
       unsigned char crc_byteH = 0;
       unsigned char crc_byteL = 0;
       int function;
       
       bytes_required = (sizeof(struct f48_response_s) + req_msg_size);    // expected response size if echo enabled

       /* Prepare message to be sent */

       function = 48;
       message[0] = slave;       // slave address
       message[1] = function;    // function id

       CalcCRC16(message, 2, &crc_byteH, &crc_byteL); // calculate CRC16 for the message data

       message[2] = crc_byteH;	 // CRC HighByte
       message[3] = crc_byteL;   // CRC LowByte
       message[4] = (unsigned char)NULL;

       #ifdef DEBUG
       fprintf(stderr, "Request message bytes:\n");
       int cnt;
       for(cnt=0; cnt <= req_msg_size; cnt++) {
            fprintf(stderr, "Byte no.%d == [%d] (%p)\n",cnt, message[cnt], message[cnt]);
       }
       fprintf(stderr, "\n");
       #endif

       /* Send prepared message to the slave device */

       ioctl(ttyfd,TCFLSH,0);    // clear serial input buffer
       ioctl(ttyfd,TCFLSH,1);    // clear serial output buffer
       write(ttyfd,message,req_msg_size);   // send data to slave
       ioctl(ttyfd,TCFLSH,0);    // clear serial input buffer
       time(&start);		 // set the start timestamp for timeout check
            
       /* Receive response from slave */

       bytes_received = 0;
       while (bytes_received < bytes_required)
       {         
		rxchar = -1;
		read(ttyfd,&rxchar,1);
         
		if (rxchar != -1)
		{
			#ifdef DEBUG
			fprintf(stderr, "Received char no. %d = [%d] (%p)\n",bytes_received, rxchar, rxchar);
			#endif

			bytes_received++;
			if (bytes_received > req_msg_size) {
			   recv[rsp_msg_size++] = rxchar;  
			} else {
			   #ifdef DEBUG
			   fprintf(stderr, "skipping...\n");
			   #endif
			}
		}  
		// timeout check
		time(&finish);	
		diff_time = difftime(finish,start);
		if(diff_time > timeout)		
		{
			#ifdef DEBUG
			fprintf(stderr, "timeout \n");
			#endif
			*error = KDRV_TIMEOUT_ERROR;
			break;
		}
       } 
               
      /* Check for exception */
      
      if (bytes_received < bytes_required)
      {
		if (recv[1] != function)
		{
			#ifdef DEBUG
			fprintf(stderr,"exception response \n");
			#endif
			*error = KDRV_EXCEPTION_ERROR;
			return recv[2]; // return exception code
		}    
      }

       /* Decode response message */

       if (bytes_required == bytes_received)
       {
		/* Calculate and check CRC on the response message */
	        CalcCRC16(recv, rsp_msg_size-2, &crc_byteH, &crc_byteL);
	        struct f48_response_s *rsp_msg;
	        rsp_msg = (struct f48_response_s *) recv; 

		if ( (crc_byteH == rsp_msg->crc16_H) && (crc_byteL == rsp_msg->crc16_L) ) {
			#ifdef DEBUG
			fprintf(stderr, "Match!\n");
			#endif
			memcpy(f48_response, rsp_msg, rsp_msg_size); // Copy the result
			*error = KDRV_SUCCESS;
			return TRUE;
		} else {
			#ifdef DEBUG
			fprintf(stderr, "Checksum mismatch!\n");
			fprintf(stderr, "[%x%x] != [%x%x]\n", crc_byteH, crc_byteL, rsp_msg->crc16_H, rsp_msg->crc16_L);
			#endif
			*error = KDRV_CRC_ERROR;
		}
      }  
              
      /* Request failed */
      return FALSE;
} 

char *get_kdrv_error(int error) 
{
	switch(error) {
		case KDRV_TIMEOUT_ERROR:
			return "request timeout";
		case KDRV_EXCEPTION_ERROR:
			return "slave returned exception";
		case KDRV_CRC_ERROR:
			return "CRC mismatch on received message";
		default:
			return "unknown error";
	}
}

int get_channel_id(char *channel) 
{
	if (!strcmp(channel, "CH0"))  return KDRV_CHANNEL_CH0;
	if (!strcmp(channel, "P1"))   return KDRV_CHANNEL_P1;
	if (!strcmp(channel, "P2"))   return KDRV_CHANNEL_P2;
	if (!strcmp(channel, "T"))    return KDRV_CHANNEL_T;
	if (!strcmp(channel, "TOB1")) return KDRV_CHANNEL_TOB1;
	if (!strcmp(channel, "TOB2")) return KDRV_CHANNEL_TOB2;

	return -1;
}
